package com.zhu.tool_test.util;


import com.zhu.tool_test.exception.graphCalException;
import com.zhu.tool_test.starke.util.lang.Doubles;


import java.awt.geom.Point2D;
import java.util.LinkedList;
import java.util.List;

/**
 * @program: smartpark-service-ep
 * @description: 图形计算工具类
 * @author: ggBall
 * @create: 2021-01-15 11:45
 **/
public class GraphCalUtils {

    /*
     * 大地坐标系资料WGS-84 长半径a=6378137 短半径b=6356752.3142 扁率f=1/298.2572236
     */
    /** 长半径a=6378137 */
    private static double a = 6378137;
    /** 短半径b=6356752.3142 */
    private static double b = 6356752.3142;
    /** 扁率f=1/298.2572236 */
    private static double f = 1 / 298.2572236;

    // 地球半径
    private static final double EARTH_RADIUS = 6370996.81;

    private static final Double PI = Math.PI;

    private static final Double PK = 180 / PI;

    /**
    * @Description 根据 圆心原点坐标 角度偏移量，半径计算出 扇形另外两个点坐标
    * @Author  ggBall
    * @Date   2021/1/15 11:53
    * @Param
     * @param centerPoint 中心点位
     * @param offsetAngle 偏移角度
     * @param radius 半径 单位 km
     * @param initDirection 初始方向
    * @Return      java.lang.Double[][]
    * @Exception
    *
    */
    public static List<Point2D.Double> getFanShapedPoint(Point2D.Double centerPoint, Double offsetAngle, Double radius, Double initDirection) throws graphCalException {
        // 1. 创建 中心点位坐标
        double xCenter = centerPoint.x;
        double yCenter = centerPoint.y;

        if (Doubles.isNullOrEmpty(xCenter) || Doubles.isNullOrEmpty(yCenter)) {
            throw new graphCalException("中心点位 信息不完全");
        }

        Point2D.Double center = new Point2D.Double(xCenter, yCenter);
        // 2. 计算 扇形的除圆心外的两个坐标
        Point2D.Double targetPoint1 = getTargetPoint(center, initDirection, radius);
        // 加入偏移角度 计算坐标
        Point2D.Double targetPoint2 = getTargetPoint(center, initDirection+offsetAngle, radius);

        LinkedList<Point2D.Double> points = new LinkedList<>();
        points.add(targetPoint1);
        points.add(targetPoint2);

        return points;
    }

    /**
    * @Description 判断 目标点位 是否在 扇形区域内
     * 1. 判断目标点位与圆心的距离 必须小于等于 扇形的半径
     * 2. 以一条边作为x轴，求出目标点位与x轴之间的夹角，小于扇形的角度
     * 以上条件必须满足 才返回 true 否则 返回 false
    * @Author  ggBall
    * @Date   2021/1/15 12:01
    * @Param
     * @param radius 半径
     * @param centerPoint 中心点位
     * @param targetPoint 目标点位
     * @param fanShapedPoint 扇形两边点位
    * @Return      java.lang.Boolean
    * @Exception
    *
    */
    public Boolean isWithin(Double radius,Double[] centerPoint,Double[] targetPoint,Double[][] fanShapedPoint) {
        return null;
    }


    /**
    * @Description 已知一个点坐标 和角度 和距离 求另一个点的坐标
    * @Author  ggBall
    * @Date   2021/1/15 14:45
    * @Param
     * @param centerPoint 已知点坐标
     * @param ang 角度
     * @param dist 距离
    * @Return      java.awt.geom.Point2D.Double
    * @Exception
    *
    */
    public static Point2D.Double getTargetPoint(Point2D.Double centerPoint,Double ang,Double dist) throws graphCalException {


        double lon = centerPoint.x;
        double lat = centerPoint.y;

        if (Doubles.isNullOrEmpty(ang)) {
            throw new graphCalException("角度不存在");
        }

        if (Doubles.isNullOrEmpty(dist) && dist > 0) {
            throw new graphCalException("距离不存在");
        }

        double alpha1 = rad(ang);
        double sinAlpha1 = Math.sin(alpha1);
        double cosAlpha1 = Math.cos(alpha1);

        double tanU1 = (1 - f) * Math.tan(rad(lat));
        double cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1));
        double sinU1 = tanU1 * cosU1;
        double sigma1 = Math.atan2(tanU1, cosAlpha1);
        double sinAlpha = cosU1 * sinAlpha1;
        double cosSqAlpha = 1 - sinAlpha * sinAlpha;
        double uSq = cosSqAlpha * (a * a - b * b) / (b * b);
        double A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
        double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));

        double cos2SigmaM=0;
        double sinSigma=0;
        double cosSigma=0;
        double sigma = dist / (b * A), sigmaP = 2 * Math.PI;
        while (Math.abs(sigma - sigmaP) > 1e-12) {
            cos2SigmaM = Math.cos(2 * sigma1 + sigma);
            sinSigma = Math.sin(sigma);
            cosSigma = Math.cos(sigma);
            double deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)
                    - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
            sigmaP = sigma;
            sigma = dist / (b * A) + deltaSigma;
        }

        double tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1;
        double lat2 = Math.atan2(sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1,
                (1 - f) * Math.sqrt(sinAlpha * sinAlpha + tmp * tmp));
        double lambda = Math.atan2(sinSigma * sinAlpha1, cosU1 * cosSigma - sinU1 * sinSigma * cosAlpha1);
        double C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
        double L = lambda - (1 - C) * f * sinAlpha
                * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));

        double revAz = Math.atan2(sinAlpha, -tmp); // final bearing

        System.out.println(revAz);
        System.out.println(lon+deg(L)+","+deg(lat2));

        return new Point2D.Double(lon+deg(L),deg(lat2));

    }


    /**
     * @Description: 第一种方法
     * @param lat_a
     * @param lng_a
     * @param lat_b
     * @param lng_b
     * @param @return
     * @return double
     * @author 钟志铖
     * @date 2014-9-7 上午10:11:35
     */
    public static double getDistanceFromTwoPoints(double lat_a, double lng_a, double lat_b, double lng_b) {
        double t1 = Math.cos(lat_a / PK) * Math.cos(lng_a / PK) * Math.cos(lat_b / PK) * Math.cos(lng_b / PK);
        double t2 = Math.cos(lat_a / PK) * Math.sin(lng_a / PK) * Math.cos(lat_b / PK) * Math.sin(lng_b / PK);
        double t3 = Math.sin(lat_a / PK) * Math.sin(lat_b / PK);

        double tt = Math.acos(t1 + t2 + t3);

        System.out.println("两点间的距离：" + 6366000 * tt + " 米");
        return 6366000 * tt;
    }

    /**
     * @Description: 第二种方法
     * @param lat1
     * @param lng1
     * @param lat2
     * @param lng2
     * @return void
     * @author 钟志铖
     * @date 2014-9-7 上午10:11:55
     */
    public static double distanceOfTwoPoints(double lat1, double lng1, double lat2, double lng2) {
        double radLat1 = rad(lat1);
        double radLat2 = rad(lat2);
        double a = radLat1 - radLat2;
        double b = rad(lng1) - rad(lng2);
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
                + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
        s = s * EARTH_RADIUS;
        s = Doubles.toFixed(s,8);
        //double ss = s * 1.0936132983377;
        //System.out.println("两点间的距离是：" + s + "米" );
        return s;
    }

    /**
    * @Description 使用数学的方法计算需要画扇形的圆弧上的点坐标
    * @Author  ggBall
    * @Date   2021/1/18 10:03
    * @Param
     * @param lat1
     * @param lng1
     * @param dist
     * @param ang
    * @Return      java.lang.Double[]
    * @Exception
    *
    */
    public static Double[] offSetBearing(Double lat1, Double lng1,Double dist, Integer ang) {
        Double lonConv = distanceOfTwoPoints(lat1,lng1,lat1,lng1+0.1) * 10 ;
        Double latConv = distanceOfTwoPoints(lat1,lng1,lat1+0.1,lng1) * 10;

        double lat = dist * Math.sin(ang * Math.PI / 180) / latConv;
        double lon = dist * Math.sin(ang * Math.PI / 180) / lonConv;

        return new Double[]{lon+lng1,lat+lat1};
    }

    /**
    * @Description 得出 扇形上的坐标集合
    * @Author  ggBall
    * @Date   2021/1/18 10:15
    * @Param
     * @param lat1
     * @param lng1
     * @param dist
     * @param sDegree
     * @param eDegree
    * @Return      java.util.List<java.lang.Double[]>
    * @Exception
    *
    */
    public static List<Double[]> sector (Double lat1, Double lng1,Double dist,Integer sDegree ,Integer eDegree) {
        LinkedList<Double[]> list = new LinkedList<>();
        int step =1;

        for (int i = sDegree; i < eDegree+1; i+=step) {

            list.add(offSetBearing(lat1,lng1,dist,i));
            // System.out.println("i = " + offSetBearing(lat1, lng1, dist, step).toString());
        }

        return list;
    }


   /**
   * @Description 度换成弧度
   * @Author  ggBall
   * @Date   2021/1/15 15:51
   * @Param
    * @param d
   * @Return      double
   * @Exception
   *
   */
    private static double rad(double d) {
        return d * Math.PI / 180.0;
    }

   /**
   * @Description 弧度换成度
   * @Author  ggBall
   * @Date   2021/1/15 15:51
   * @Param
    * @param x
   * @Return      double
   * @Exception
   *
   */
    private static double deg(double x) {
        return x * 180 / Math.PI;
    }


    public static void polarTest() {

        double ang = 46d;
        double r = 1000d;

        double cx = 0d;
        double cy = 0d;
        double p1x = 3.0d;
        double p1y = 3.0d;
        double ux = r * Math.cos(ang) + cx;
        double uy = r * Math.sin(ang) + cy;
        // 求目标点位 与 中心点位 的 弧度
        double v = Math.atan2(p1y - cy, p1x - cx);
        // 返回 偏移角度 中间的角度 的弧度
        double v1 = Math.atan2(ux, uy);

        // v 在 v1-ang 和 v1+ang 之间

        boolean flag = v > v1-ang && v1-ang > -Math.PI && v < v1+ang && v1+ang < Math.PI ;
        System.out.println("flag = " + flag);

    }

    public static void polarTest2 (Double cLat,Double cLon,Double tLat,Double tLon,Double r,Double ang) {
        // 1.判断 两点之间 距离 是否小于 半径 小于半径则满足条件 大于半径则不满足条件
        double dist = distanceOfTwoPoints(cLat, cLon, tLat, tLon);

        // 2. 判断 目标点位的夹角小于 扇形的角度
        double diff = Math.abs(tLat - cLat);
        double r1 = Math.sqrt(Math.pow(tLat - cLat, 2) + Math.pow(tLon - cLon, 2));
        double acos = Math.acos(diff - r1);

        boolean flag = dist < r && acos < ang;
        System.out.println("flag = " + flag);

    }


}
